package node;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Calendar;
import java.util.Iterator;
import discovery.DiscoveryProxyFactory;
import event.EQueueProxyFactory;
import event.Event;
import fileserver.FileServer;
import task.Task;

public class NodeExecWorker implements Runnable{
	protected InetAddress 	discoveryAddress;
	protected int 			discoveryPort;
	protected Task			currentTask;
	
	protected NodeExecWorkerListener workerDelegate;
	
	public NodeExecWorker(Task t, InetAddress discoveryAddress, int discoveryPort)
	{
		super();
		this.currentTask		= t;
		this.discoveryAddress 	= discoveryAddress;
		this.discoveryPort 		= discoveryPort;
		
		this.workerDelegate 	= null;
	}
	
	public void setWorkerDelegate(NodeExecWorkerListener delegate)
	{
		this.workerDelegate = delegate;
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		try{
			String cmd = currentTask.getCommand();
		
			//NON DEVO SCARICARE NESSUN FILE
			if(currentTask.getFiles().isEmpty()==true){
				//file dei risultati
				String filename =  String.valueOf(currentTask.getId()) + String.valueOf(Calendar.getInstance().getTimeInMillis()) + ".txt";
				//concateno il comando al path dei risultati
				cmd = cmd + " >> " + "resources/"+filename;
				//se non devo scaricare file eseguo il task
				exec_task_send_event(cmd, currentTask, filename);
			}
			else
			{
				String[] components;
				String filename_for_command;
				//DEVO SCARICARE IL FILE PRIMA DI ESEGUIRE IL TASK
				Iterator<String> it = currentTask.getFiles().iterator();
				while(it.hasNext())
				{
					String[] tokens;
					tokens = it.next().split(":");
					String ip_server = tokens[0];
					int server_port = Integer.parseInt(tokens[1]);
					String file_to_download = tokens[2];
					components = file_to_download.split("/");
					filename_for_command = components[components.length-1];
					//SCARICO IL FILE
					download_file(file_to_download, ip_server, server_port);
					String concat = "download/" + filename_for_command;
					cmd = cmd.replaceAll(filename_for_command, concat);
		
				}
				//file dei risultati
				String filename_results =  String.valueOf(currentTask.getId()) + String.valueOf(Calendar.getInstance().getTimeInMillis()) + ".txt";
				//concateno il comando al path dei risultati
				String command = cmd + " >> " + "resources/"+ filename_results;
				//ESEGUO IL TASK E INVIO L'EVENTO DI FINE ESECUZIONE
				exec_task_send_event(command, currentTask, filename_results);
			}           
		} catch (Exception exp){
			System.out.println(" >> NODE - NodeExecWorker: Generic error: " + exp.getMessage());
		}	
		
		//if(this.workerDelegate!=null)
		//{
			synchronized (this.workerDelegate){
				this.workerDelegate.workerHasFinished(this);
			}
		//}
	}
	
	/**
	 * Esegue il task e invia l'evento di fine esecuzione
	 * 
	 * @param cmd
	 * @param tempTask
	 * @param filename
	 */
	public void exec_task_send_event(String cmd, Task tempTask, String filename){
		
		try{
			//eseguo il comando contenuto nel task
			ProcessBuilder pb = new ProcessBuilder("bash", "-c",cmd);
	        pb.start();
	       
	        //eseguo una lookup per farmi tornare il nodedescriptor da cui prendere il nodo a cui inviare l'evento
	        String remoteDiscovery = discoveryAddress + ":" + discoveryPort + ":UDP";
	        String lookupRequestString = "IDNODE:"+String.valueOf(tempTask.getOwner())+",";
	        NodeDescriptor source_node = DiscoveryProxyFactory.getInstance().getProxy(remoteDiscovery).lookup("MYID:"+NodeDescriptor.getInstance().getId()+","+lookupRequestString);
	        System.out.println("Richiesta Lookup event: MYID:"+NodeDescriptor.getInstance().getId()+","+lookupRequestString);
	        if(source_node!=null)
	        {
		        //creo la stringa contenente la porta dell'enqueue del destinatario per il proxy 
		        String receiver_event = source_node.getIp() + ":" + String.valueOf(source_node.getEES().getPort()) + ":UDP";
		      
		        //creo il content dell'event per il download dei file dei risultati
		        String content_event = NodeDescriptor.getInstance().getIp()+ ":" + NodeDescriptor.getInstance().getFsPort() +":" +FileServer.resources+"/"+ filename;
		
		        //creo l'evento
		        Event event = new Event(Event.EVENT_RESULT,  content_event);
		        int status =	EQueueProxyFactory.getInstance().getProxy(receiver_event).enqueue(event);
		        System.out.println(status+": valore status");
		        if(status == 0)
		        {
		        	//l'enqueue e' andata a buon fine
		        	System.out.println(" >> NODE: NodeExecWorker: Event Submitted");
		        }
		        else
		        {
		        	//l'enqueue non e' andata a buon fine
		        	System.out.println(" >> NODE: NodeExecWorker: ERR - Cannot submit Event");
		        }
	        }
	        else
	        	System.out.println(" >> NODE: NodeExecWorker: Lookup - No Results");
		}
		catch(Exception e)
		{
				e.printStackTrace();
		}	
	}
	
	
	/**
	 * Metodo per il download dei file.
	 * 
	 */
	
	public int download_file(String filename, String ip_server, int server_port){
		try{
			long start = System.currentTimeMillis();

			    Socket sock = new Socket(ip_server,server_port);
			    System.out.println("Connecting...");

			    //TODO: inserire invio stringa "download" e ricezione ACK
			    DataOutputStream outToServer = new DataOutputStream(sock.getOutputStream());
			    outToServer.writeBytes(filename + "\n");

			    String[] tokens = new String[1];
			    tokens = filename.split("/");
			    filename = tokens[1];
			    String path = FileServer.download + "/" + filename;
			
			    FileOutputStream fos = new FileOutputStream(new File(path));
			    InputStream in = sock.getInputStream();
			    byte[] data = new byte[1024];
			  
				for(int readNum; (readNum = in.read(data))!=-1;)
				{
					 fos.write(data,0,readNum);
				}
				   
			    long end = System.currentTimeMillis();
			    System.out.println(end-start);
			    fos.close();
			    in.close();
			    sock.close();
			    return 0;
			}
			catch (Exception e) 
			{
				System.out.println(e.getMessage());
				return -1;	
			} 
	}
}
